using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Net;
using System.Diagnostics;

namespace TALoaders
{
    class OandaFXLoader : BaseLoader
    {
        private System.Globalization.CultureInfo usCultureM = new System.Globalization.CultureInfo("en-US");
        // example: http://www.oanda.com/currency/historical-rates?date_fmt=us&date=06/25/10&date1=06/01/10&exch=EUR&expr=CZK&margin_fixed=0&format=CSV&redirected=1
        private string urlHistDataTmplM = "http://www.oanda.com/currency/historical-rates?date_fmt=us&date=[endDate]&date1=[startDate]&exch=[baseCcy]&expr=[priceCcy]&margin_fixed=0&format=CSV&redirected=1";

        // Note: we use Yahoo to get latest price
        private string urlQuoteTmplM = "http://quote.yahoo.com/d/quotes.csv?s=[ticker]&f=[tags]";

        private WebClient webClientM = new WebClient();
        private TraceSwitch traceM = new TraceSwitch("OandaFXLoader", "Debug essages in OandaFXLoader");

        public OandaFXLoader()
            : base(Loader.OandaFX)
        { }

        ~OandaFXLoader()
        {
            webClientM.Dispose();
        }

        /// <summary>Get historical quotes. ONLY CLOSE PRICES.</summary>
        /// <param name="ticker">6 chars security ticker: e.g. EURUSD</param>
        /// <remarks>Oanda provides only close prices, high, low, open and volume are bogus</remarks>
        override public SortedList<DateTime, Quote> getHistoricalPrices(string ticker, DateTime startDate, DateTime endDate)
        {
            // --->> prepare URL <<---
            if (ticker.Length < 6)
                throw new Exception("Oanda ticker must have at least 6 characters. t: " + ticker);

            string baseCcy = ticker.Substring(0, 3);
            string priceCcy = ticker.Substring(3, 3);

            SortedList<DateTime, Quote> result = new SortedList<DateTime, Quote>();
            // Iterate over 500-days periods and append them to the result list
            DateTime startPeriod;
            DateTime endPeriod = startDate.AddDays(-1);
            do
            {
                startPeriod = endPeriod.AddDays(1);  // avoid having duplicate entries
                endPeriod = endPeriod.AddDays(499);  // Oanda does not allow more than 500 data point at the same time
                if (endPeriod < endDate)
                    this.addMax500ClosePrices(baseCcy, priceCcy, startPeriod, endPeriod, result);
                else // endPeriod >= endDate
                    this.addMax500ClosePrices(baseCcy, priceCcy, startPeriod, endDate, result);
            }
            while (endPeriod < endDate);

            return result;

        }

        /// <summary>Perform Error handling and get single time-series of dated historical prices (based on QuoteType)</summary>
        /// <param name="ticker">Security ticker</param>
        /// <param name="startDate">historical prices start date</param>
        /// <param name="endDate">historical prices end date</param>
        /// <param name="type">quoteType e.g. Close</param>
        /// <returns>Dated time series of QuoteType values</returns>
        override public SortedList<DateTime, double> getHistoricalPrices(string ticker, DateTime startDate, DateTime endDate, QuoteType type)
        {
            if (type != QuoteType.Close && type != QuoteType.AdjClose)
                throw new Exception("OandaFXLoader supports only Close or AdjClose QuoteType. Type " + type.ToString() + " is unsupported");
            return base.getHistoricalPrices(ticker, startDate, endDate, type);
        }

        // Note: Using Yahoo to get latest prices
        override public double getLastPrice(string ticker, out string timeStamp)
        {
            // for full list of tags see: http://www.gummy-stuff.org/Yahoo-data.htm
            string yTicker = ticker.Substring(0, 6) + "=X";
            string url = urlQuoteTmplM.Replace("[tags]", "l1").Replace("[ticker]", yTicker);
            string sLastTrade = webClientM.DownloadString(url).Replace("\r", "").Replace("\n", "");

            url = urlQuoteTmplM.Replace("[tags]", "t1").Replace("[ticker]", ticker);
            Debug.WriteLineIf(traceM.TraceInfo, url);
            timeStamp = webClientM.DownloadString(url).Replace("\r", "").Replace("\n", "");

            return Convert.ToDouble(sLastTrade, usCultureM);
        }

        /// <summary>Oanda allows for max 500 quotes on a single call. This funciton loads up to 500 quotes</summary>
        /// <param name="baseCcy">"Commodity" currency</param>
        /// <param name="priceCcy">price currency</param>
        /// <param name="startDate">start date</param>
        /// <param name="endDate">end date</param>
        /// <param name="prices">The list of dated prices where the result will be added</param>
        private void addMax500ClosePrices(string baseCcy, string priceCcy, DateTime startDate, DateTime endDate, SortedList<DateTime, Quote> quotes)
        {
            string sDate = startDate.ToString("MM\"/\"dd\"/\"yy", usCultureM);
            string eDate = endDate.ToString("MM\"/\"dd\"/\"yy", usCultureM);
            string url = urlHistDataTmplM.Replace("[baseCcy]", baseCcy).Replace("[priceCcy]", priceCcy).Replace("[startDate]", sDate).Replace("[endDate]", eDate);

            // --->> Download Data <<---
            Debug.WriteLineIf(traceM.TraceInfo, url);
            string htmlPage = webClientM.DownloadString(url);
            // --->> Parse HTML Page <<-- Get raw data whic arre between <PRE> and </PRE>
            Regex regex = new Regex("<PRE>(.*)</PRE>", RegexOptions.Singleline);
            Match m = regex.Match(htmlPage); // TODO check isMatch first...
            if (m.Groups.Count < 2 || m.Groups[1].Length == 0)
                throw new Exception("Failed parsing ticker " + baseCcy + priceCcy + " from Oanda");

            string[] rows = m.Groups[1].ToString().Replace("\r", "").Split('\n');
            int nOfRows = rows.Length - 1; // remove last row which is Empty

            // Create result
            
            for (int i = 0; i < nOfRows; ++i)
            {
                string[] rData = rows[i].Split(',');
                DateTime date = Convert.ToDateTime(rData[0], usCultureM);
                double price = Convert.ToDouble(rData[1], usCultureM);
                Quote quote = new Quote(price, price, price, price, 0, price);  // Only close price available at Oanda
                quotes.Add(date, quote);
            }
        }
    }
}
